home *** CD-ROM | disk | FTP | other *** search
/ Reverse Code Engineering RCE CD +sandman 2000 / ReverseCodeEngineeringRceCdsandman2000.iso / RCE / Library / Assembly Programming Journal / apj_4.txt < prev    next >
Encoding:
Text File  |  2000-05-25  |  49.5 KB  |  1,205 lines

  1. ::/ \::::::.
  2. :/___\:::::::.
  3. /|    \::::::::.
  4. :|   _/\:::::::::.
  5. :| _|\  \::::::::::.                                               Apr-June  99
  6. :::\_____\::::::::::.                                              Issue      4
  7. ::::::::::::::::::::::.........................................................
  8.  
  9.             A S S E M B L Y   P R O G R A M M I N G   J O U R N A L
  10.                       http://asmjournal.freeservers.com
  11.                            asmjournal@mailcity.com
  12.  
  13.  
  14.  
  15.  
  16. T A B L E   O F   C O N T E N T S
  17. ----------------------------------------------------------------------
  18. Introduction...................................................mammon_
  19.  
  20. "Using COM in Assembly Language"..........................Lord.Lucifer
  21.  
  22. "Stack Frames and High-Level Calls"............................mammon_
  23.  
  24. "Define Your Memory".......................................Alan Baylis
  25.  
  26. "Writing a Boot Sector in A86"...........................Jan Verhoeven
  27.  
  28. "A Basic Virus Writing Primer"...................................Chili
  29.  
  30. Column: Win32 Assembly Programming
  31.     "Mouse Input....".........................................Iczelion
  32.     "Menus"...................................................Iczelion
  33.  
  34. Column: The C standard library in Assembly
  35.     "C string functions:_strtok"................................Xbios2
  36.  
  37. Column: The Unix World
  38.     "Using Menus in Xt"........................................mammon_
  39.  
  40. Column: Assembly Language Snippets
  41.     "Triple XOR".........................................Jan Verhoeven
  42.     "Trailing Calls".....................................Jan Verhoeven
  43.  
  44. Column: Issue Solution
  45.     "Fire Demo"....................................................iCE
  46. ----------------------------------------------------------------------
  47.        +++++++++++++++++++++Issue  Challenge+++++++++++++++++++
  48.        Write a "Fire Demo"-style program in less than 100 bytes
  49. ----------------------------------------------------------------------
  50.  
  51.  
  52. ::/ \::::::.
  53. :/___\:::::::.
  54. /|    \::::::::.
  55. :|   _/\:::::::::.
  56. :| _|\  \::::::::::.
  57. :::\_____\:::::::::::..............................................INTRODUCTION
  58.                                                                      by mammon_
  59.  
  60.  
  61. In the last few months I have come across a number of links to APJ, and have
  62. received the proverbial ton of email regarding it. Strangely enough, the
  63. majority of these tend to agree that the one problem with the journal is its
  64. infrequent --if not irregular-- publication. If that is the only complaint so
  65. far, I think I can cope with it ;)
  66.  
  67. This issue is, naturally, very late due to what could be called "real world"
  68. [lit., "that which does not go away when a power outtage kills your PC"]
  69. considerations; however the articles by weight alone should make up for some
  70. of this.
  71.  
  72. The largest of the bunch is undoubtedly the virus writing tutorial by Chili,
  73. who may have beat my previous record for article length: a very thorough work,
  74. worth reading just to help protect against virii, if not to write them. This
  75. is accompanied by Jan's discussion of boot sector programming...a suitable
  76. companion article, I believe.
  77.  
  78. High-level coders will undoubtedly be interested in Lord Lucifer's article on
  79. COM programming in assembly; it seems that high-level areas such as COM,
  80. DirectDraw, and Winsock coding are starting to receive a fair degree of
  81. attention from the assembly language world, judging from the tutorials I have
  82. been coming across.
  83.  
  84. Xbios2 has continued his excellent C stdlib work, and Icezlion has contributed
  85. two more of his now-legendary Win32 asm tutorials; I of course have kept up
  86. the Unix vanguard with yet another Xt article.
  87.  
  88. This month's challenge was contributed by iCE, and had a .text-size I could
  89. not readily beat.
  90.  
  91. A few brief notes concerning the web page: I have thrown together a basic
  92. collection of assembly language links at
  93.         http://asmjournal.freeservers.com/lynx.html
  94. Submissions for this links page are welcome. I have also been getting a few
  95. emails to the APJ inbox asking or offering help with assembly language; since
  96. I check the inbox fortnightly at best, I have added a "classified ads" page to
  97. the APJ website at
  98.         http://www.guestbook4free.com/en/28806/entries/
  99. which is essentially a guestbook where people can post contact info, projects
  100. they need help with, etc ... more or less a one-way bulletin board like, well,
  101. like classified ads are.
  102.  
  103. That should just about wrap things up. Enjoy the issue!
  104.  
  105. _m
  106.  
  107.  
  108. ::/ \::::::.
  109. :/___\:::::::.
  110. /|    \::::::::.
  111. :|   _/\:::::::::.
  112. :| _|\  \::::::::::.
  113. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  114.                                                  Using COM in Assembly Language
  115.                          by Lord Lucifer
  116.  
  117.  
  118. This article will discuss how to use COM interfaces in your assembly language
  119. programs.  It will not discuss what COM is and how it is used, but rather how
  120. it can be used when programming in assembler.  It will discuss only how to
  121. use existing interfaces, and not how to actually implement new ones; this will
  122. be shown in a future atricle.
  123.  
  124.  
  125. About COM
  126. ------------------------------------------------------------------------------
  127.  
  128. Here is a brief introduction to the basics behind COM.
  129.  
  130. A COM object is one in which access to an object's data is achieved
  131. exclusively through one or more sets of related functions. These function
  132. sets are called interfaces, and the functions of an interface are called
  133. methods. COM requires that the only way to gain access to the methods of an
  134. interface is through a pointer to the interface.
  135.  
  136. An interface is actually a contract that consists of a group of related
  137. function prototypes whose usage is defined but whose implementation
  138. is not. An interface definition specifies the interface's member functions,
  139. called methods, their return types, the number and types of their parameters,
  140. and what they must do. There is no implementation associated with an
  141. interface. An interface implementation is the code a programmer supplies to
  142. carry out the actions specified in an interface definition.
  143.  
  144. An instance of an interface implementation is actually a pointer to an array
  145. of pointers to methods (a function table that refers to an implementation of
  146. all of the methods specified in the interface). Any code that has a pointer
  147. through which it can access the array can call the methods in that interface.
  148.  
  149.  
  150.  
  151. Using a COM object assembly language
  152. -------------------------------------------------------------------------------
  153.  
  154. Access to a COM object occurs through a pointer.  This pointer points to a
  155. table of function pointers in memory, called a virtual function table, or
  156. vtable in short.  This vtable contains the addresses of each of the objects
  157. methods. To call a method, you indirectly call it through this pointer table.
  158.  
  159. Here is an example of a C++ interface, and how its methods are called:
  160.  
  161.         interface IInterface
  162.         {
  163.              HRESULT QueryInterface( REFIID iid, void ** ppvObject );
  164.              ULONG AddRef();
  165.              ULONG Release();
  166.              Function1( INT param1, INT param2);
  167.              Function2( INT param1 );
  168.         }
  169.  
  170.         // calling the Function1 method
  171.         pObject->Function1( 0, 0);
  172.  
  173. Now here is how the same functionality can be implemented using assembly
  174. language:
  175.  
  176.         ; defining the interface
  177.         ; each of these values are offsets in the vtable
  178.         QueryInterface          equ             0h
  179.         AddRef                  equ             4h
  180.         Release                 equ             8h
  181.         Function1               equ             0Ch
  182.         Function2               equ             10h
  183.  
  184.         ; calling the Function1 method in asm
  185.         ; the method is called by obtaining the address of the objects
  186.         ; vtable and then calling the function addressed by the proper
  187.         ; offset in the table
  188.         push    param2
  189.         push    param1
  190.         mov     eax, pObject
  191.         push    eax
  192.         mov     eax, [eax]
  193.         call    [eax + Function1]
  194.  
  195. You can see this is somewhat different than calling a function normally.
  196. Here, pObject points to the Interface's vTable.  At the Function1(0Ch) offset
  197. in this table is a pointer to the actual function we wish to call.
  198.  
  199.  
  200.  
  201. Using HRESULT's
  202. -------------------------------------------------------------------------------
  203.  
  204. The return value of OLE APIs and methods is an HRESULT. This is not a handle
  205. to anything, but is merely a 32-bit value with several fields encoded in the
  206. value.  The parts of an HRESULT are shown below.
  207.  
  208. HRESULTs are 32 bit values layed out as follows:
  209.  
  210.  3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
  211.  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
  212. +-+-+-+-+-+---------------------+-------------------------------+
  213. |S|R|C|N|r|    Facility         |               Code            |
  214. +-+-+-+-+-+---------------------+-------------------------------+
  215.  
  216.  S - Severity Bit
  217.      Used to indicate success or failure
  218.      0 - Success
  219.      1 - Fail
  220.  
  221.      By noting that this bit is actually the sign bit of the 32-bit value,
  222.      checking success/failure is simply performed by checking its sign:
  223.  
  224.      call       ComFunction        ; call the function
  225.      test       eax,eax            ; now check its return value
  226.      js         error              ; jump if signed (meaning error returned)
  227.      ; success, so continue
  228.  
  229.  R - reserved portion of the facility code, corresponds to NT's
  230.      second severity bit.
  231.  
  232.  C - reserved portion of the facility code, corresponds to NT's
  233.      C field.
  234.  
  235.  N - reserved portion of the facility code. Used to indicate a
  236.      mapped NT status value.
  237.  
  238.  r - reserved portion of the facility code. Reserved for internal
  239.      use. Used to indicate HRESULT values that are not status
  240.      values, but are instead message ids for display strings.
  241.  
  242.  Facility - is the facility code
  243.      FACILITY_WINDOWS    = 8
  244.      FACILITY_STORAGE    = 3
  245.      FACILITY_RPC        = 1
  246.      FACILITY_WIN32      = 7
  247.      FACILITY_CONTROL    = 10
  248.      FACILITY_NULL       = 0
  249.      FACILITY_ITF        = 4
  250.      FACILITY_DISPATCH   = 2
  251.  
  252.      To retreive the Facility,
  253.  
  254.      call       ComFunction    ; call the function
  255.      shr        eax, 16        ; shift the HRESULT to the right by 16 bits
  256.      and        eax, 1FFFh     ; mask the bits, so only the facility remains
  257.      ; eax now contains the HRESULT's Facility code
  258.  
  259.  Code - is the facility's status code
  260.  
  261.      To get the Facility's status code,
  262.      call       ComFunction             ; call the function
  263.      and        eax, 0000FFFFh          ; mask out the upper 16 bits
  264.      ; eax now contains the HRESULT's Facility's status code
  265.  
  266.  
  267.  
  268. Using COM with MASM
  269. ------------------------------------------------------------------------------
  270. If you use MASM to assemble your programs, you can use some of its
  271. capabilities to make calling COM functions very easy.  Using invoke, you can
  272. make COM calls look almost as clean as regular calls, plus you can add type
  273. checking to each function.
  274.  
  275.  
  276. Defining the interface:
  277.  
  278.      IInterface_Function1Proto     typedef proto :DWORD
  279.      IInterface_Function2Proto     typedef proto :DWORD, :DWORD
  280.  
  281.      IInterface_Function1          typedef ptr IInterface_Function1Proto
  282.      IInterface_Function2          typedef ptr IInterface_Function2Proto
  283.  
  284.      IInterface struct DWORD
  285.            QueryInterface          IUnknown_QueryInterface         ?
  286.            AddRef                  IUnknown_AddRef                 ?
  287.            Release                 IUnknown_Release                ?
  288.            Function1               IInterface_Function1            ?
  289.            Function2               Interface_Function2             ?
  290.      IInterface ends
  291.  
  292. Using the interface to call COM functions:
  293.  
  294.      mov     eax, pObject
  295.      mov     eax, [eax]
  296.      invoke  (IInterface [eax]).Function1, 0, 0
  297.  
  298. As you can see, the syntax may seem a bit strange, but it allows for a simple
  299. method using the function name itself instead of offsets, as well as type
  300. checking.
  301.  
  302.  
  303.  
  304. A Sample program written using COM
  305. ------------------------------------------------------------------------------
  306.  
  307. Here is some sample source code which uses COM written in straight assembly
  308. language, so it should be compatable with any assembler you prefer with only
  309. minor changes necessary.
  310.  
  311. This program uses the Windows Shell Interfaces to show the contents of the
  312. Desktop folder in a window.  The program is not complete, but shows how the
  313. COM library is initialized, de-initialized, and used. I also shows how the
  314. shell library is used to get folders and obcets, and how to perform
  315. actions on them.
  316.  
  317.  
  318. ..386
  319. ..model flat, stdcall
  320.  
  321. include windows.inc             ; include the standard windows header
  322. include shlobj.inc              ; this include file contains the shell namespace
  323.                                 ; definitions and constants
  324.  
  325. ;----------------------------------------------------------
  326. ..data
  327.         wMsg                    MSG     <?>
  328.         g_hInstance             dd      ?
  329.         g_pShellMalloc          dd      ?
  330.  
  331.         pshf                    dd      ?       ; shell folder object
  332.         peidl                   dd      ?       ; enum id list object
  333.  
  334.         lvi                     LV_ITEM <?>
  335.         iCount                  dd      ?
  336.         strret                  STRRET  <?>
  337.         shfi                    SHFILEINFO <?>
  338.         ...
  339.  
  340. ;----------------------------------------------------------
  341. ..code
  342. ; Entry Point
  343. start:
  344.     push    0h
  345.     call    GetModuleHandle
  346.     mov     g_hInstance,eax
  347.  
  348.     call    InitCommonControls
  349.  
  350. ; initialize the Component Object Model(COM) library
  351. ; this function must be called before any COM functions are called
  352.     push    0
  353.     call    CoInitialize
  354.     test    eax,eax                         ; error when the MSB = 1
  355.                                             ; (MSB = the sign bit)
  356.     js      exit                            ; js = jump if signed
  357.  
  358. ; Get the Shells IMalloc object pointer, and save it to a global variable
  359.     push    offset g_pShellMalloc
  360.     call    SHGetMalloc
  361.     cmp     eax, E_FAIL
  362.     jz      shutdown
  363.  
  364.  
  365. ; here we would set up the windows, list view, message loop, and so on....
  366. ; we would also call the FillListView procedure...
  367. ; ....
  368.  
  369.  
  370. ; Cleanup
  371. ; Release IMalloc Object pointer
  372.     mov     eax, g_pShellMalloc
  373.     push    eax
  374.     mov     eax, [eax]
  375.     call    [eax + Release]         ; g_pShellMalloc->Release();
  376.  
  377. shutdown:
  378. ; close the COM library
  379.     call    CoUninitialize
  380.  
  381. exit:
  382.     push    wMsg.wParam
  383.     call    ExitProcess
  384. ; Program Terminates Here
  385.  
  386.  
  387. ;----------------------------------------------------------
  388. FillListView proc
  389.  
  390. ; get the desktop shell folder, saved to pshf
  391.     push    offset pshf
  392.     call    SHGetDesktopFolder
  393.  
  394. ; get the objects of the desktop folder using the EnumObjects method of
  395. ; the desktop's shell folder object
  396.     push    offset peidl
  397.     push    SHCONTF_NONFOLDERS
  398.     push    0
  399.     mov     eax, pshf
  400.     push    eax
  401.     mov     eax, [eax]
  402.     call    [eax + EnumObjects]
  403.  
  404. ; now loop through the enum id list
  405. idlist_loop:
  406. ; Get next id list item
  407.     push    0
  408.     push    offset pidl
  409.     push    1
  410.     mov     eax, peidl
  411.     push    eax
  412.     mov     eax, [eax]
  413.     call    [eax + Next]
  414.     test    eax,eax
  415.     jnz     idlist_endloop
  416.  
  417.     mov     lvi.imask, LVIF_TEXT or LVIF_IMAGE
  418.     mov     lvi.iItem,
  419.  
  420. ; Get the item's name by using the GetDisplayNameOf method
  421.     push    offset strret
  422.     push    SHGDN_NORMAL
  423.     push    offset pidl
  424.     mov     eax, pshf
  425.     push    eax
  426.     mov     eax, [eax]
  427.     call    [eax + GetDisplayNameOf]
  428. ; GetDisplayNameOf returns the name in 1 of 3 forms, so get the correct
  429. ; form and act accordingly
  430.     cmp     strret.uType, STRRET_CSTR
  431.     je      strret_cstr
  432.     cmp     strret.uType, STRRET_OFFSET
  433.     je      strret_offset
  434.  
  435. strret_olestr:
  436.     ; here you could use WideCharToMultiByte to get the string,
  437.     ; I have left it out because I am lazy
  438.     jmp     strret_end
  439.  
  440. strret_cstr:
  441.     lea     eax, strret.cStr
  442.     jmp     strret_end
  443.  
  444. strret_offset:
  445.     mov     eax, pidl
  446.     add     eax, strret.uOffset
  447.  
  448. strret_end:
  449.     mov     lvi.pszText, eax
  450.  
  451. ; Get the items icon
  452.     push    SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_ICON
  453.     push    sizeof SHFILEINFO
  454.     push    offset shfi
  455.     push    0
  456.     push    pidl
  457.     call    SHGetFileInfo
  458.     mov     eax, shfi.iIcon
  459.     mov     lvi.iImage, eax
  460.  
  461. ; now add item to the list
  462.     push    offset lvi
  463.     push    0
  464.     push    LVM_INSERTITEM
  465.     push    hWndListView
  466.     call    SendMessage
  467.  
  468. ; repeat the loop
  469. idlist_endloop:
  470.  
  471. ; now free the enum id list
  472. ; Remember all allocated objects must be released...
  473.     mov     eax, peidl
  474.     push    eax
  475.     mov     eax,[eax]
  476.     call    [eax + Release]
  477.  
  478. ; free the desktop shell folder object
  479.     mov     eax, pshf
  480.     push    eax
  481.     mov     eax,[eax]
  482.     call    [eax + Release]
  483.  
  484.     ret
  485. FillListView endp
  486.  
  487.  
  488. END start
  489.  
  490.  
  491. Conclusion
  492. -------------------------------------------------------------------------------
  493.  
  494. Well, that is about it for using COM with assembly language.  Hopefully, my
  495. next article will go into how to define your own interfaces.  As you can
  496. see, using COM is not difficult at all, and with it you can add a very
  497. powerful capability to your assembly language programs.
  498.  
  499.  
  500.  
  501. ::/ \::::::.
  502. :/___\:::::::.
  503. /|    \::::::::.
  504. :|   _/\:::::::::.
  505. :| _|\  \::::::::::.
  506. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  507.                                                Stack Frames and High-Level Calls
  508.                                                by mammon_
  509.  
  510.  
  511. Last month I covered how to implement high-level calls in Nasm. Since then it
  512. has come to my attention that many beginning programmers are unfamiliar with
  513. calling conventions and the stack frame; to remedy this I have prepared a brief
  514. discussion of these topics.
  515.  
  516. The CALL Instruction
  517. --------------------
  518. At its most basic, an assembly language call takes this for:
  519.     push [parameters]
  520.     call [address]
  521. Some assemblers will require that the CALL statement take as an rgument only
  522. addresses leading to external functions or addresses created with a macro or
  523. directive such as PROC. However, as a quick glance through a debugger or a
  524. passing familiarity with Nasm will demonstrate, the CALL instruction simply
  525. jumps to an address [often a label in the source code] while pushing the
  526. contents of EIP [containing the address of the instruction following the call]
  527. onto the stack. The CALL instruction is therefore equivalent to the following
  528. code:
  529.     push EIP
  530.     jmp  [address]
  531.  
  532. The address that has been called will thefore have the stack set up as follows:
  533.     [Last Parameter Pushed]: DWORD
  534.     [Address of Caller]    : DWORD
  535.     ---  "Top" of Stack [esp]  ---
  536. At this point, anything pushed onto the stack will be on top of [that is, with a
  537. lower memory address, since the stack "grows" downwards] the return address.
  538.  
  539. The Stack Frame
  540. ---------------
  541. Note that the parameters to the call therefore cannot be POPed from the stack,
  542. as this will destroy the saved return address and thus cause the application to
  543. crash upon returning from the call [unless, of course, a chosen return address
  544. is PUSHed onto the stack before returning from the call]. The logical way to
  545. reference these parameters, then, would be as offsets from the stack pointer:
  546.     [parameter 2]      : DWORD esp + 8
  547.     [parameter 1]      : DWORD esp + 4
  548.     [Address of Caller]: DWORD esp
  549.     -----  "Top" of Stack [esp]  -----
  550. In this example, "parameter 1" is the parameter pushed onto the stack last, and
  551. "parameter 2" is the parameter pushed onto the stack before parameter 1, as
  552. follows:
  553.     push [parameter 2]
  554.     push [parameter 1]
  555.     call [procedure]
  556. The problem with referring to parameter as offsets from esp is that esp will
  557. change whenever a value is PUSHed onto the stack during the routine. For this
  558. reason, it is standard for routines which take parameters to set up a "stack
  559. frame".
  560.  
  561. In a stack frame, the base pointer [ebp] is set equal to the stack pointer [esp]
  562. at the start of the call; this provides a "base" address from which parameters
  563. can be addressed as offsets. It is assumed that the caller had a stack frame
  564. also; thus the value of ebp must be preserved in order to prevent causing damage
  565. to the caller. The stack frame usually takes the following form:
  566.     push ebp
  567.     mov  ebp, esp
  568.     ... [actual code for the routine] ...
  569.     mov  esp, ebp
  570.     pop  ebp
  571. This means that once the stack frame has been entered, the stack has the
  572. following structure:
  573.     [parameter 2]      : DWORD ebp + 12
  574.     [parameter 1]      : DWORD ebp + 8
  575.     [Address of Caller]: DWORD ebp + 4
  576.     [Old Base Pointer] : DWORD ebp
  577.     -----   Base Pointer [ebp]   -----
  578.     -----  "Top" of Stack [esp]  -----
  579. The use of the base pointer also allows space to  be allocated on the stack for
  580. local variables. This is done by simply subtracting bytes from esp; since esp is
  581. restored when the stack frame is exitted, this space will automatically be
  582. deallocated. The local variables are then referred to as *negative* offsets from
  583. ebp; these may be EQUed to meaningful symbol names in the source code. A routine
  584. that has 3 local DWORD variables would take the following form:
  585.      Var1 EQU [ebp-4]
  586.      Var2 EQU [ebp-8]
  587.      Var3 EQU [ebp-12]        ;provide meaningful names for the variables
  588.     push ebp
  589.     mov  ebp, esp
  590.     sub  esp, 3*4       ;3 DWORDs at 4 BYTEs apiece
  591.     ... [actual code for the routine] ...
  592.     mov  esp, ebp
  593.     pop  ebp
  594. This routine would then have the following stack structure after the allocation
  595. of the local variables:
  596.     [parameter 2]      : DWORD ebp + 12
  597.     [parameter 1]      : DWORD ebp + 8
  598.     [Address of Caller]: DWORD ebp + 4
  599.     [Old Base Pointer] : DWORD ebp
  600.     -----   Base Pointer [ebp]   -----
  601.     [Var1]             : DWORD ebp - 4
  602.     [Var2]             : DWORD ebp - 8
  603.     [Var3]             : DWORD ebp - 12
  604.     -----  "Top" of Stack [esp]  -----
  605.  
  606. The stack frame has can also be used to provide a call trace, as it stores the
  607. base pointer of [and thus a pointer to the caller of] the caller. Assume that a
  608. program has the following flow of execution:
  609. proc_1:    push dword call1_p2
  610.     push dword call1_p1
  611.     call proc_2
  612. ________proc_2:    push call2_p1
  613.         call proc_3
  614. ________________proc_3:    push call3_p1
  615.             call proc_4
  616. Upon creation of the stack frame in proc_4, the stack has the following
  617. structure:
  618.     [call1_p2]             : DWORD ebp + 36
  619.     [call1_p1]             : DWORD ebp + 32
  620.     [Return Addr of Call1] : DWORD ebp + 28
  621.     [Old Base Pointer]     : DWORD ebp + 24
  622.     ----  Base Pointer of Call 1  ----
  623.     [call2_p1]             : DWORD ebp + 20
  624.     [Return Addr of Call2] : DWORD ebp + 16
  625.     [Base Pointer of Call1]: DWORD ebp + 12
  626.     ----  Base Pointer of Call 2  ----
  627.     [call3_p1]             : DWORD ebp + 8
  628.     [Return Addr of Call3] : DWORD ebp + 4
  629.     [Base Pointer of Call2]: DWORD ebp
  630.     -----   Base Pointer [ebp]   -----
  631.     -----  "Top" of Stack [esp]  -----
  632. As you can see, for each previous call the return address is [ebp+4], where ebp
  633. is the address of the saved base pointer for the call previous to that one.
  634. Thus, if one could traverse the history of stack frames as follows:
  635.     mov eax, ebp        ; eax = address of previous ebp
  636.     mov ecx, 10        ; trace the last 10 calls
  637. loop_start:
  638.     mov ebx, [eax+4]    ; ebx = return address for call
  639.     call print_stack_trace
  640.     mov eax, [eax]        ; step back one stack frame
  641.     loop loop_start
  642. This is exceptionally useful for exception handling; the handling function will
  643. be able to print out a stack history to aid debugging. This principle can also
  644. be applied in conjunction with debugging code [for example, the Win32 debug API]
  645. to create a utility which will trace the calls [in reality, the stack frames of
  646. the calls] made by a target. Essentially, this would boil down to the following
  647. logic:
  648.     1) Breakpoint on changes to EBP
  649.     2) On Break, get return address [ebp+4]
  650.     3) Get instruction prior to return address
  651.     4) Print or log the instruction
  652. Note that this can be enhanced to resolves symbol names in the logged CALL
  653. instruction, such that local or API address labels [e.g. GetWindowTextA] can be
  654. logged rather than just the address itself.
  655.  
  656. The ENTER Instruction
  657. ---------------------
  658. The ENTER instruction is used to create a stack frame with a single instruction;
  659. it is equivalent to the code
  660.     push ebp
  661.     mov  ebp, esp
  662. The ENTER instruction takes a first parameter that specifes the number of bytes
  663. to reserve for local variables; an optional second parameter gives the nesting
  664. level [0-31] of the current stack frame in the overall program structure. This
  665. is often used by high-level languages to save call trace information for error
  666. handlers, as it specifies the number of additional [previous] stack frame pointers
  667. to save on the stack.
  668.  
  669. The RET Instruction
  670. -------------------
  671. Any routine which is accessed by a CALL instruction must be terminated with a
  672. return [RET] instruction. As one can see from the operation of the CALL
  673. instruction, if you were to attempt to circumvent the RET instruction by JMPing
  674. to the retrun address, the stack would still be corrupted. The RET statement is
  675. roughly equivalent to the following code:
  676.     pop  EIP
  677.  
  678. Note that the RET must take place after exiting the stack frame in order to
  679. avoid corruption of the stack.
  680.  
  681. The LEAVE Instruction
  682. ---------------------
  683. The LEAVE instruction is used to exit a stack frame created with the ENTER
  684. instruction; it is equivalent to the code
  685.     mov  esp, ebp
  686.     pop  ebp
  687. The LEAVE instruction takes no parameters and still requires a RET statement to
  688. follow it.
  689.  
  690. High-level Language Calling Conventions
  691. ---------------------------------------
  692. At this point one may wonder what has happened to the parameters pushed onto the
  693. stack prior to the call. Are they still on the stack after the RET, or have they
  694. been cleared? Since the parameters cannot be POPed from the stack while within
  695. the call, they still are on the stack at the RET instruction.
  696.  
  697. At this point the programmer has two options.  They can have the caller clean up
  698. the stack by adding the number of bytes pushed to esp immediately after the
  699. call:
  700.     push dword param2
  701.     push dword param1
  702.     call procedure
  703.     add  esp, 2 * 4         ;2 DWORDs at 4 BYTEs apiece
  704. Or they can clear the stack by passing to the RET instruction the number of
  705. bytes that need to be cleared:
  706.     push dword param2
  707.     push dword param1
  708.     call procedure
  709.     ...
  710. procedure:
  711.     push ebp
  712.     mov  ebp, esp
  713.     ...
  714.     mov  esp, ebp
  715.     pop  ebp
  716.     ret  8                ;2 DWORDs at 4 BYTEs apiece
  717. Which method is chosen is left up to the programmer; however, when writing a
  718. library or API, one must make clear who is responsible for cleaning up the
  719. stack. In addition, when interfacing with high-leve languages, one also has to
  720. make clear which order the parameters are to be pushed in. For this reason there
  721. are calling conventions    for the high-level languages.
  722.  
  723. The C calling convention is used to interface with the C and C++ programming
  724. languages; it is used in the standard C library and in Unix APIs. It pushes the
  725. parameters from right to left, and does not clean up the stack upon return from
  726. the call. A call to a C-style routine would look as follows:
  727.     ;corresponds to the C code
  728.     ;procedure(param1, param2)
  729.     push dword param2
  730.     push dword param1
  731.     call procedure
  732.     add  esp, 8
  733. A C-style routine would have the following structure:
  734.     push ebp
  735.     mov  ebp, esp
  736.     ...
  737.     mov  esp, ebp
  738.     pop  ebp
  739.     ret
  740.  
  741. The Pascal calling convention is used interface with the Pascal, BASIC, and
  742. Fortran programming languages; it is used in the Win16 API. It pushes the parameters
  743. from left to right, and cleans up the stack upon return from the call; as such
  744. it is the opposite of the C convention. A call to a Pascal routine would look as
  745. follows:
  746.     ;corresponds to the C code
  747.     ;procedure(param1, param2)
  748.     push dword param1
  749.     push dword param2
  750.     call procedure
  751. A Pascal-style routine would have the following structure:
  752.     push ebp
  753.     mov  ebp, esp
  754.     ...
  755.     mov  esp, ebp
  756.     pop  ebp
  757.     ret 8        ;clear the 2 dword parameters
  758.  
  759. The Stdcall ["standard call" or __stdcall] calling convention is a combination
  760. of the C and Pascal conventions; it is used in the Win32 API. It pushes the
  761. parameters from right to left, and cleans the stack upon return from the call. A
  762. call to a Stdcall routine would look as follows:
  763.     ;corresponds to the C code
  764.     ;procedure(param1, param2)
  765.     push dword param2
  766.     push dword param1
  767.     call procedure
  768. A Stdcall-style routine would have the following structure:
  769.     push ebp
  770.     mov  ebp, esp
  771.     ...
  772.     mov  esp, ebp
  773.     pop  ebp
  774.     ret 8
  775.  
  776. There is also a Register calling convention [also called "fastcall"] which uses
  777. registers rather than the stack to pass parameters. The first parameter is
  778. passed in eax, the second in EDX, and the third in EBX; subsequent parameters
  779. are passed via the stack. A call to a Register routine would look as follows:
  780.     ;corresponds to the C code
  781.     ;procedure(param1, param2, param3)
  782.     mov  eax, param1
  783.     mov  edx, param2
  784.     mov  ebx, param3
  785.     call procedure
  786. Note that there is no defined standard method of clearing the stack ro the
  787. Register convention; however most implemntations clear the stack in the Pascal
  788. style.
  789.  
  790.  
  791.  
  792. ::/ \::::::.
  793. :/___\:::::::.
  794. /|    \::::::::.
  795. :|   _/\:::::::::.
  796. :| _|\  \::::::::::.
  797. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  798.                                                              Define Your Memory
  799.                                                              by Alan Baylis
  800.  
  801.  
  802. [I am going to preface this article with a brief note, since it is not
  803.  covering assembly language per se, but rather a utility that will be of use
  804.  to asm coders. The author sums it up well in his original email to me:
  805.   "Define is a new type of assembler/disassembler that does not use source
  806.    code. The program reads the byte values in memory and checks a library to
  807.    find a definition that describes the byte values it reads. The library can
  808.    be added to and is used as a permanent macro list to write instuctions,
  809.    functions, etc to memory. Most assemblers also use standard 3 character
  810.    mnemonics to descibe the instruction set, however, with Define you can
  811.    rename the instructions and your own macros to anything and up to 250
  812.    characters."
  813.  Sounds pretty promising.
  814.   _m                    ]
  815.  
  816.  
  817. For the x86 series of processor I have been working on a new type of assembler and have
  818. written a program called Define. The program could be called a sketch of what a future
  819. version might be like. The program is fully workable but suffers from a few limitations,
  820. the first is that it is written in QBASIC which may be a blow to devoted machine coders,
  821. and the second is that it can only comfortably use about three hundred definitions
  822. (Definitions are like a library of machine code macros and I'll discuss them more fully
  823. later) and a third limitation, not to its functionality, is that the program doesn't have
  824. a quick mouse and menu driven interface, but I'm working on it.
  825.  
  826. I liked the idea of macros and saw the neccessity for using them so that I and others
  827. don't have to "reinvent the wheel" as it has been put, but I wanted a way to see the
  828. machine code instructions and the byte values that made up the macro. This can't be done
  829. through using source code as the finished code is generated at the discretion of the
  830. compilers authors and requires a debugger to verify its content.
  831.  
  832. To make what was originally intended to be a debugger but without the source code I
  833. decided to make a program that could read memory and interpret the byte values it finds
  834. into their mnemonic equivalents or better (much like a debugger), so that while reading
  835. memory, if the program found the byte value 205 followed by the value 5 it would display
  836. "INT 5". To do this I needed what I termed a 'definiton' which included the byte values
  837. that make up an instruction or small macro and included a description or name for the
  838. function they perform.
  839.  
  840. Unlike what I had done with a previous assembler I decided to put the definitions in a
  841. separate file rather than include them as data within the main program, this allowed
  842. for the addition or removal of future definitions. I then quickly realised that since
  843. these definitions contained the byte values of an instruction, then they could also be
  844. used to write the bytes into memory. I added  functions to save and load programs as
  845. well as functions to manipulate the definition file and the program was underway.
  846.  
  847. I found while writing the definitions for the instruction set that it would be good
  848. (and necessary) if the program could read an instruction even if one of the bytes is
  849. unknown or variable; I decided to call these bytes undefined bytes, so that if the
  850. program found the number 205 it would display something like "Interrupt call" regard-
  851. less of what number followed.
  852.  
  853. While reading memory I also wanted a way to exclude data areas from being interpreted
  854. into definitions, so I added a new definition type called addresses which contain the
  855. address of the first and last bytes of a data area and a name to describe the data area.
  856. If these are turned on in the program then they are used instead of the normal definitions
  857. when reading that part of memory.
  858.  
  859. To then take Define closer to being an assembler rather than a debugger I also included
  860. labels that label memory addresses and the destination of jump and branch instructions.
  861.  
  862. I envision that a future version of Define written in machine code or a similar program
  863. will have a pop up list of definitons and use a point and click method of writing the
  864. code as opposed to the current method of scrolling through them from a different page.
  865. The future version will also need to be able to handle thousands of definitions as
  866. opposed to the few hundred it can use at a time now, in order to accommodate situations
  867. such as the following:
  868.  
  869. To call the interrupt 21h,9 which prints a string it is necessary to put the function
  870. number 9 in AH and the address of the string in the registers DS:DX and then call the
  871. interrupt,
  872.  
  873. MOV AH,9
  874. MOV DX,address
  875. INT 21h
  876.  
  877. however it is also valid to put the number 9 in AH after the address of the string has
  878. been put in DS:DX,
  879.  
  880. MOV DX,address
  881. MOV AH,9
  882. INT 21h
  883.  
  884. To make a definition for this interrupt at least two definitions will need to be made
  885. and therefore a larger definition file. This also doesn't account for the situation in
  886. which the number 9 may have been filled three instructions earlier and is assumed to be
  887. correct at the time when the interrupt is called, in this case only the definitions for
  888. the instructions will be seen and not a definition for the interrupt.
  889.  
  890. One of the best aspects to Define in my book is that the memory can be viewed according
  891. to a persons level of understanding (or will be as the definitions are written,) for
  892. example the program is able to only show definitions of a certain level and no other. I
  893. have chosen to represent the level of a definition by its color, I have used blue (1)
  894. for the lowest level which are the instruction set definitions and then green (2) for
  895. the next level which are the DOS, BIOS, etc definitions and then magenta (3) for the
  896. next level which may be definitions to clear the screen and print the date combined and
  897. so on, so that a person who knows little about machine code may set the maximum definition
  898. color to red (4) and still be able to write a program using Define. The advantage for
  899. those who know machine code is that they need not be restricted to only a high level
  900. definition, by turning the observance of the color off they can press the letter B when
  901. viewing a  high level definition and see the lower level definitions that make up the
  902. higher one. By repeatedly pressing B they can view the program as level 1 (blue) or even
  903. as the byte values themselves.
  904.  
  905. The most radical departure from most assemblers is that when writing a program the program
  906. is composed in memory,  the byte values of the definitions are written directly to an
  907. unused or reserved area of memory where they can be further altered directly while
  908. reading memory. This could also be said to be the most dangerous method as it can easily
  909. lead to the accidental writing of other areas of memory, while this is true I have also
  910. found a benefit, if Define is stopped and then restarted the program being written will
  911. still be in memory without having been saved (depending on where in memory the program is
  912. being written.)
  913.  
  914. The maker of a violin, while demonstrating it, must have said at one time or another "A
  915. good violinist could really show you how to play it", I too like the maker of a violin am
  916. sure there are better definition writers than myself. To become a high level language the
  917. high level definitions need to be written and I ask any person who has a passion for writing
  918. hand written code to send me a definition or two to include in the definition file.
  919.  
  920.  
  921. You can download Define from my homepage at
  922.     http://members.net-tech.com.au/alaneb/default.htm
  923. and there is a step by step guide to using the program in the zip file called manual.doc.
  924.  
  925. Please send any definitions or reponse to Alan at alien1_3@excite.com
  926.  
  927.  
  928.  
  929. ::/ \::::::.
  930. :/___\:::::::.
  931. /|    \::::::::.
  932. :|   _/\:::::::::.
  933. :| _|\  \::::::::::.
  934. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  935.                                                     Writing a Boot Sector in A86
  936.                                                     by Jan Verhoeven
  937.  
  938.  
  939. I have been coding for FreeDOS some time, but that is a C project and I
  940. rather hate C. It is so clumsy. That's also why I always code in A86
  941. assembly language. The "No Red Tape" assembler that makes life a lot
  942. easier for programmers.
  943.  
  944. A86 is good. The debugger (D86) could be better, but not too much. I
  945. registered my version and I want to encourage everyone to follow my
  946. lead. The software is good enough to pay for it. And it ensures proper
  947. development of the software. If you can spare 20 bucks a month for the
  948. ISP, you should also spend this on quality software.
  949. During the last two years I have been submiutting bugs to Isaacson and
  950. all of them have been fixed in the latest version (4.03).
  951.  
  952. Besides A86 being the best assembler around, it has some idiosyncracies
  953. to which some people need to get used to. Plus my personal preferences,
  954. which might add to that...
  955.  
  956.  - When I refer to a memory location I use square brackets.
  957.  - I use single quotes for texts
  958.  - I use most of the A86 features.
  959.  
  960. Some of the A86 features are:
  961.  
  962.  - very powerful macro language
  963.  - numbers starting with a ZERO are ALWAYS hex, no matter how they end
  964.  - easy IF statements to reduce nonsense labelnames
  965.  - local labels, like below: only two local labels.
  966.  
  967. I started out on the Z-8000, back in 1981, switched to the Z-80, Z-8,
  968. 8086, PIC 16Cxx, some 8051 (Barffff), some 68K (yummie yummie). Mainly
  969. in ASM and else in Modula-2. I have some really cool and useful routines
  970. lying around for DOS. And I'm gonna share them with the world.
  971.  
  972. The following code is a bootsector which can be used for noon-bootable
  973. disks. In this case for a 1.44 Mb floppy disk. You could use it to make
  974. a commercial out of every non-bootable disk.
  975.  
  976. First the code:
  977.  
  978. ----- Code file -------------------------------------------------
  979. name     flopnb
  980. title    Floppy disk boot sector, non-bootable, 1.44 Mb
  981. page     80, 120
  982.  
  983. ; version 1.0  : It works                               : OK 12-12-1998
  984.  
  985. lf       = 10
  986. cr       = 13
  987.  
  988.          org   0
  989.  
  990.          jmp   short main       ; this is critical!
  991.          nop                    ; and this too!
  992. ; ----------------------
  993.  
  994. OEMname  db    'StupiDOS'
  995. BpS      dw    512              ; bytes per sector
  996. SpA      db    1                ; sectors per allocation unit (=cluster)
  997. ResSect  dw    1                ; reserved sectors, starting from sector 0
  998. NrFats   db    2                ; number of FAT's on this disk
  999. FiR      dw    224              ; number of entries in ROOT directory
  1000. Total    dw    2880             ; number of sectors per disk
  1001. ToM      db    0F0              ; Type of Media
  1002. SpF      dw    9                ; Sectors per Fat
  1003. SpT      dw    18               ; sectors per Track
  1004. Heads    dw    2                ; number of heads
  1005. Hidden   dw    0, 0             ; Hidden sectors
  1006. GrandTot dd    0                ; total for disks over 32 Mb
  1007. IntId    db    0, 0
  1008. BootSign db    029              ; extended boot signature
  1009. VolumeID dd    0566E614A        ; serial number ...
  1010. DiskLabl db    'DOS is MINE'    ; volume label
  1011. FATtype  db    'FAT-12  '       ; FAT type
  1012.          db    'VeRsIoN=1.0', 0 ; for version control only
  1013. ; ----------------------
  1014.  
  1015. L1:      push  si               ; stack up return address
  1016.          ret                    ; and jump to it
  1017.  
  1018. print:   pop   si               ; this is the first character
  1019.          mov   bx, 0            ; video page 0
  1020. L0:      lodsb                  ; get token
  1021.          cmp   al, 0            ; end of string?
  1022.          je    L1               ; if so, exit
  1023.          mov   ah, 0E           ; else print it
  1024.          int   010              ; via TTY mode
  1025.          jmp   L0               ; until done
  1026. ; ----------------------
  1027.  
  1028. main:    cld                    ; init direction flag
  1029.          cli                    ; take care of 1 faulty batch of 88's in 1980
  1030.          mov   ax, 07C0         ; this is the segmentvalue at start
  1031.          mov   ds, ax           ; store it in DS, ES
  1032.          mov   es, ax
  1033.          mov   ax, 0            ; clear ax ...
  1034.          mov   ss, ax           ; ... to prime the SS register
  1035.          mov   sp, 07C00        ; set stackpointer
  1036.          sti                    ; OK, interrupts may come again
  1037.          call  print            ; show that message
  1038.          db    cr
  1039.          db    'This is not a bootable floppy. '
  1040.          db    'Please strike any key to reboot.', cr, lf
  1041.          db    'This floppy disk is formatted by FreeDOS', cr, lf, lf
  1042.          db    'Please visit us at www.freedos.org', cr, lf, 0
  1043.  
  1044. L0:      mov   ah, 1            ; wait for keypress by ...
  1045.          int   016              ; ...  interrogating keyboard
  1046.          jz    L0               ; if no key pressed, loop back
  1047.          mov   ax, 0            ; else address system variables
  1048.          mov   es, ax           ; in order to ...
  1049.       es mov   w [0472], 01234  ; signal: NO POST and go on ...
  1050.          jmp   0FFFF:0000       ; with the next reboot
  1051.  
  1052.          org   01FE             ; look for the dotted line and ...
  1053.          db    055, 0AA         ; ... don't forget to sign!
  1054.  
  1055. ------------------------------------------------- Code file -----
  1056.  
  1057. The first three lines are straightforward: name, title and page. Not
  1058. much to tell about that. Then some version info for the programmer, some
  1059. equates and the ORG statement.
  1060.  
  1061. If no ORG is supplied, A86 will assume it is ORG 0100. I ordered an ORG   0,
  1062. which means several things:
  1063.  
  1064.  - start assembly at address 0
  1065.  - the output file will be called *.BIN
  1066.  
  1067. Bootsectors must start with some particular bytes. Therefore the first
  1068. three bytes need to be either a short jump, a variable offset plus a
  1069. NOP. Or a (long) jump without a NOP.
  1070.  
  1071. At offset 03 of the bootsector starts the DPB (Disk Parameter Block)
  1072. which tells the OS what kind of disk this is. It starts off with an OEM
  1073. name. Please put ASCII in there, or virus scanners might trip on it with
  1074. a "Bloodhound warning".
  1075.  
  1076. After the description of the geometry of this disk, I included an
  1077. extended boot signature, since we have ample room left. It contains
  1078. Volume ID, Disk Label, and FAT-type strings.
  1079.  
  1080. The PRINT subroutine is a nice one. It will print the ASCIIZ string that
  1081. follows it. This is quite a handy routine since you can simply change
  1082. messages without having to worry about the address and length of the
  1083. actual message.
  1084.  
  1085. Print is called like this:
  1086.  
  1087.         call   print
  1088.         db     'Hello World', cr, lf, 0
  1089.         ...
  1090.  
  1091. Print takes the "return address" off the stack. This of course is no
  1092. return address but the address of the message. What follows is easy:
  1093.  
  1094.  - get next character
  1095.  - IF  (non-zero)  print character  ELSE  leave loop  ENDIF
  1096.  - the current si pointer is the actual return address... So we push it
  1097.  - and return to caller.
  1098.  
  1099. Perhaps a jmp  si could be possible too, but I like clear code, in most
  1100. cases. If you need obfuscated code, switch to C. :)
  1101.  
  1102. The actual program is very simple. It just sets up a stack and the
  1103. segment registers, and then prints that it will do nothing. Gee, what a
  1104. life...
  1105.  
  1106. After the message we wait for a key and next signal:
  1107.  
  1108.  - fast reboot
  1109.  - jump to the reboot vector
  1110.  
  1111. Whatever there will be between end of code and offset 01FE is not
  1112. relevant (it could be your ad) but the last two bytes of the boot sector
  1113. must be a valid boot signature.
  1114.  
  1115. That's it. With this code you can make your own custom non-bootsector.
  1116.  
  1117. I hope this software has also shown that linking and assuming are
  1118. supported by A86, but certainly not necessary. Also, this software does
  1119. not rely on any HLL calls. It's just assembly language as it should be.
  1120.  
  1121. I want to remark that this software is Open Source, according to the rules
  1122. of the GNU GPL. Make sure you understand these rules before embedding this
  1123. routine in your own software.
  1124.  
  1125.  
  1126.  
  1127. ::/ \::::::.
  1128. :/___\:::::::.
  1129. /|    \::::::::.
  1130. :|   _/\:::::::::.
  1131. :| _|\  \::::::::::.
  1132. :::\_____\:::::::::::...........................................FEATURE.ARTICLE
  1133.                                                    A Basic Virus Writing Primer
  1134.                                                    by Chili
  1135.  
  1136.  
  1137. What horror  must the ignorant victim  undergo as it  becomes aware  of a being
  1138. that lives inside its own body, growing ever stronger, reproducing itself until
  1139. its host, unable to bear more finally colapses and dies an horrible death. What
  1140. panic  it must  feel,  knowing nothing  can be  done  in time  to avoid  such a
  1141. terrible fate.  A predator so tiny, that unsuspectedly it spreads from one host
  1142. to  another,  by  so  rapidly  infecting  millions.  An  organism,  so  utterly
  1143. resourceful and small, that it stays most of the time undetectable, breeding in
  1144. the shadows.
  1145.  
  1146. Computer viruses aren't much  different from their biological counterpart,  but
  1147. instead of infecting cells they infect files and boot sectors.  In this article
  1148. I'll try to explain the basics of file viruses,  more specifically runtime (aka
  1149. direct  action)  COM  infectors.   This  will  cover  most  simple  search  and
  1150. replication  methods used and  is only to be  considered as an  introduction to
  1151. virus writing.  After some thought I've decided not  to include any full source
  1152. code  for a  working virus,  since anyone  with  half  a brain  and a  somewhat
  1153. mediocre  knowledge of assembly can  easily build a virus out  of the pieces of
  1154. code  that will  be presented.  Furthermore  it's not  my wish to  increase the
  1155. number of viruses in the wild, thing that would undoubtedly happen by the hands
  1156. of some I-have-no-brain-and-can't-program-hellspawn bent on random destruction.
  1157. Anyway, on with the article...
  1158.  
  1159.  
  1160. Some Sort Of 'Programming Virii Safely' Guide
  1161. ---------------------------------------------
  1162. The only really safe way  to program viruses  is to know what you're  doing and
  1163. understand at  every time how the virus is behaving.  If you test a virus on your
  1164. own machine without fully comprehending its ins and outs, then you will most
  1165. likely have your system trashed. It would be best if you had a second computer
  1166. just for this purpose, since a buggy programming can lead to a lot of crashes
  1167. and general havoc.  If not, a Ramdrive can be created and a Subst can be done,
  1168. so that all accesses to  physical drives are  redirected to the virtual one.
  1169. Assuming that you want your Ramdrive to have 512-byte sectors, a limit of 1024
  1170. entries and to allocate 2048K of extended memory,  you must add this line to your
  1171. CONFIG.SYS:
  1172.  
  1173. DEVICE=C:\DOS\RAMDRIVE.SYS 2048 512 1024 /E
  1174.  
  1175. Then you must copy COMMAND.COM  and SUBST.EXE to the Ramdrive so that DOS won't
  1176. hang and also in order for you to be able to delete all redirections when done.
  1177. And to associate all  physical drives to the newly  created virtual drive  (and
  1178. assuming that it is D: and all your drives are A: and C:) you should do:
  1179.  
  1180. SUBST A: D:\
  1181. SUBST C: D:\
  1182.  
  1183. Of course this last method isn't  perfect. You should always know how to
  1184. completely remove a virus before running it, or you'll end cleaning up the mess
  1185. for quite some time.
  1186.  
  1187. Just use  common sense.  For example,  if  you're  writing a  virus aimed  at a
  1188. specific file type,  all you have  to do is copy all files of that  type you do
  1189. not wish to  be infected to a different extension and when  you're done testing
  1190. just  switch those files  back to their original extension.  While testing  you
  1191. should  also place breakpoints  and warning  messages throughout  the code,  so
  1192. that you know at all times what  the virus is doing as well as it will help you
  1193. debugging it. Also you should program and test different routines separately as
  1194. it will reduce complexity and bug proneness.  Lastly the use of memory and disk
  1195. mapping/editing utilities,  a set of good anti-virus and most important the use
  1196. of backups is encouraged, so that you can keep track  of things and are able to
  1197. restore your system in case something goes wrong.
  1198.  
  1199. In case things  get really out of hand  you should always  have a clean "rescue
  1200. disk" which you should  create by doing a FORMAT A: /S /U and then copying into
  1201. it some  useful DOS files  like FORMAT.COM,  UNFORMAT.COM, FDISK.EXE,  SYS.COM,
  1202. MEM.EXE,  ATTRIB.EXE, DEBUG.EXE,  CHKDSK.EXE, SUBST.EXE,  a text editor just in
  1203. case and whichever other  files you may find useful.  Also an anti-v
  1204.  
  1205.